// Package system_service_v1
// @Author zhongxc
// @Date 2024/7/2 17:16:00
// @Desc
package system_service_v1

import (
	"context"
	"errors"
	"fmt"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"log"
	"reflect"
	"time"
	"unicode/utf8"
	"vben-admin/pkg/dao"
	"vben-admin/pkg/model"
	"vben-admin/pkg/repository"
	"vben-admin/pkg/svc"
	systemService "vben-grpc-common/vben-admin/types/system.service.v1"
)

type SystemService struct {
	systemService.UnimplementedSystemServiceServer
	svcCtx *svc.ServiceContext
	dao    repository.DepartmentRepo
}

func NewSystemService(svcCtx *svc.ServiceContext) *SystemService {
	return &SystemService{
		svcCtx: svcCtx,
		dao:    dao.NewDepartmentDao(svcCtx.Db),
	}
}

func (s *SystemService) getDeptList(deptList []*model.DepartmentModel, parentId int64) []*systemService.DeptListData {
	if deptList == nil {
		return nil
	}
	var deptListData []*systemService.DeptListData
	for _, val := range deptList {
		statusInt := int32(val.Status)
		createdAt := val.CreatedAt.Format(time.DateTime)
		deptItem := &systemService.DeptListData{
			Id:        val.Id,
			Name:      val.Name,
			ParentId:  val.ParentId,
			SortNo:    val.SortNo,
			Status:    statusInt,
			CreatedAt: createdAt,
			Remark:    val.Remark,
			Children:  []*systemService.DeptListData{},
		}
		// 递归循环
		if val.ParentId == parentId {
			deptItem.Children = s.buildDeptTree(deptList, val.Id)
			deptListData = append(deptListData, deptItem)
		}
	}
	return deptListData
}

func (s *SystemService) buildDeptTree(deptMap []*model.DepartmentModel, parentId int64) []*systemService.DeptListData {
	children := make([]*systemService.DeptListData, 0)
	for _, dept := range deptMap {
		if dept.ParentId == parentId {
			statusInt := int32(dept.Status)
			createdAt := dept.CreatedAt.Format(time.DateTime)
			deptItem := &systemService.DeptListData{
				Id:        dept.Id,
				Name:      dept.Name,
				ParentId:  dept.ParentId,
				SortNo:    dept.SortNo,
				Status:    statusInt,
				CreatedAt: createdAt,
				Remark:    dept.Remark,
				Children:  children, // make([]*systemService.DeptListData, 0),
			}
			deptItem.Children = s.buildDeptTree(deptMap, dept.Id)
			children = append(children, deptItem)
		}
	}
	return children
}

// GetDeptList 查询部门列表
//
// 查询系统中的部门列表，并根据查询结果生成相应的部门列表响应。
// 如果查询成功但没有找到任何部门，将返回一个空的部门列表响应。
//
// 参数:
//
//	ctx: 上下文对象，用于传递请求相关的上下文信息。
//	req: 部门列表请求对象，包含查询参数。
//
// 返回值:
//
//	*DeptListResponse: 部门列表响应对象，包含查询结果。
//	error: 错误对象，如果查询过程中出现错误，则非空。
func (s *SystemService) GetDeptList(ctx context.Context, req *systemService.DeptListRequest) (*systemService.DeptListResponse, error) {
	fmt.Printf("name=%s, status=%d", req.Name, req.Status)

	// req.Status 校验必须是0或者1
	if req.Status != 0 && req.Status != 1 {
		return nil, status.Error(codes.InvalidArgument, "部门状态参数错误")
	}

	// 从数据访问对象中获取部门列表
	deptList, err := s.dao.GetDepartmentList(ctx, req.Name, req.Status)
	if err != nil {
		// 记录错误日志，并返回内部错误信息
		log.Println(err)
		return nil, status.Error(codes.Internal, "获取部门列表失败")
	}
	// 如果部门列表为空，返回空的响应对象
	if len(deptList) == 0 {
		return &systemService.DeptListResponse{
			Data: make([]*systemService.DeptListData, 0),
		}, nil
	}
	// 将部门列表转换为部门列表数据对象
	deptListData := s.getDeptList(deptList, 0)
	// 返回填充了部门数据的响应对象
	return &systemService.DeptListResponse{
		Data: deptListData,
	}, nil
}

// CreateDept
// @Description: 创建部门
// @receiver s
// @param ctx	上下文对象，用于传递请求相关的上下文信
// @param req	创建部门的请求，包含部门的名称、状态、父部门ID等信息。
// @return *NoRes	操作成功时返回的空响应对象。
// @return error	操作过程中可能出现的错误。
// 该方法首先验证请求中的部门名称是否为空或过长，然后转换部门状态的字符串表示为整型。
// 接着，它检查备注字段的长度是否超过限制。如果一切验证通过，它会检查是否有同名的部门已存在。
// 如果部门名称不存在重复，它将创建一个新的部门模型并保存到数据库中。
func (s *SystemService) CreateDept(ctx context.Context, req *systemService.CreateDeptRequest) (*systemService.NoRes, error) {
	// 验证部门名称是否为空
	if req.Name == "" {
		s.svcCtx.ZapLog.Error("部门名称不能为空")
		return nil, status.Error(codes.InvalidArgument, "部门名称不能为空")
	}

	// 验证部门名称长度是否超过限制
	if utf8.RuneCountInString(req.Name) > 200 {
		return nil, status.Error(codes.InvalidArgument, "部门名称字段字符长度不能超过200")
	}

	if req.Status != 0 && req.Status != 1 {
		return nil, status.Error(codes.InvalidArgument, "部门状态数据不合法")
	}

	// 验证备注字段长度是否超过限制
	if utf8.RuneCountInString(req.Remark) > 500 {
		return nil, status.Error(codes.InvalidArgument, "备注字段字符长度不能超过500")
	}

	// 检查是否有同名的部门已存在
	department, err := s.dao.GetDepartmentByName(ctx, req.Name)
	if err != nil {
		return nil, status.Error(codes.Internal, err.Error())
	}

	if department != nil {
		return nil, status.Error(codes.AlreadyExists, "部门名称重复")
		// return nil, errs.GrpcError(model.DeptNameRepeat)
	}

	// 获取当前时间，用于记录部门的创建时间，并格式化为Y-m-d
	// createdAt := time.Now().Format("2006-01-02 15:04:05")
	// createdAt := time.Now()

	// 创建一个新的部门模型，并填充请求中的信息
	departmentModel := model.NewDepartmentModel()
	departmentModel.Name = req.Name
	departmentModel.ParentId = req.ParentId
	departmentModel.SortNo = req.SortNo
	departmentModel.Status = int8(req.Status)
	departmentModel.Remark = req.Remark
	// departmentModel.CreatedAt = createdAt
	departmentModel.CreatedBy = "admin"
	// 将部门模型保存到数据库
	err = s.dao.Create(ctx, departmentModel)
	if err != nil {
		return nil, status.Error(codes.Internal, "部门新增失败")
	}
	// 操作成功，返回空响应
	return &systemService.NoRes{}, nil
}

// 添加一个用于验证 UpdateDeptRequest 的函数
func (s *SystemService) validateDeptUpdate(req *systemService.UpdateDeptRequest) error {
	if req == nil || reflect.ValueOf(req).IsNil() {
		return errors.New("请求对象不能为空")
	}

	if req.Id <= 0 {
		return errors.New("部门ID不能为空")
	}

	// 根据业务逻辑添加更多的验证逻辑...
	// 例如，验证 Name, SortNo, Status, Remark, UpdatedBy 的合法性
	if req.Name == "" {
		return errors.New("部门名称不能为空")
	}

	if req.Status != 0 && req.Status != 1 {
		return errors.New("部门状态参数错误")
	}

	if utf8.RuneCountInString(req.Remark) > 500 {
		return errors.New("备注字段字符长度不能超过500")
	}

	if req.UpdatedBy == "" {
		return errors.New("更新人字段不能为空")
	}

	return nil
}

func (s *SystemService) UpdateDept(ctx context.Context, req *systemService.UpdateDeptRequest) (*systemService.NoRes, error) {
	// 数据校验
	err := s.validateDeptUpdate(req)
	if err != nil {
		s.svcCtx.ZapLog.Error("数据校验失败", zap.Error(err))
		return nil, status.Error(codes.InvalidArgument, err.Error())
	}

	// 检查更新的数据是否存在
	department, err := s.dao.GetDepartmentById(ctx, req.Id)
	if err != nil {
		s.svcCtx.ZapLog.Error("获取部门信息失败", zap.Error(err))
		return nil, status.Error(codes.Internal, err.Error())
	}
	if department == nil {
		return nil, status.Error(codes.NotFound, "部门不存在")
	}

	data := map[string]interface{}{
		"parent_id":  req.ParentId,
		"name":       req.Name,
		"sort_no":    req.SortNo,
		"status":     req.Status,
		"remark":     req.Remark,
		"updated_by": req.UpdatedBy,
	}

	// 更新数据
	err = s.dao.UpdateById(ctx, req.Id, data)
	if err != nil {
		// 增加原始错误信息的详细度
		return nil, status.Errorf(codes.Internal, "部门更新失败: %v", err)
	}

	return &systemService.NoRes{}, nil
}

func (s *SystemService) DeleteDept(ctx context.Context, req *systemService.DeleteDeptRequest) (*systemService.NoRes, error) {
	// 参数校验
	if req.Id <= 0 {
		return nil, status.Error(codes.InvalidArgument, "部门ID不能为空")
	}

	if req.DeletedBy == "" {
		return nil, status.Error(codes.InvalidArgument, "删除人字段不能为空")
	}

	deptHasChild, err := s.dao.CheckDeptHasChild(ctx, req.Id)
	if err != nil {
		return nil, status.Error(codes.Internal, err.Error())
	}

	if deptHasChild != nil {
		return nil, status.Error(codes.FailedPrecondition, "部门下存在子部门，不能删除")
	}

	// 检查要删除的数据是否存在
	department, err := s.dao.GetDepartmentById(ctx, req.Id)
	if err != nil {
		return nil, status.Error(codes.Internal, err.Error())
	}
	if department == nil {
		return nil, status.Error(codes.NotFound, "部门不存在")
	}

	err = s.dao.DeleteById(ctx, req.Id)
	if err != nil {
		return nil, status.Error(codes.Internal, err.Error())
	}
	return &systemService.NoRes{}, nil
}

// GetDeptInfo 查询部门信息的方法
//
// 本方法接收一个DeptInfoRequest类型的请求，其中包含所需查询的部门相关信息，
// 并返回一个DeptInfoResponse类型的响应，其中包含查询到的部门信息。
// 如果查询过程中出现错误，将返回错误信息。
//
// 参数:
//
//	ctx: 上下文对象，用于传递请求相关的上下文信息，如请求ID、超时等。
//	req: 部门信息请求对象，包含查询部门所需的信息。
//
// 返回值:
//
//	*DeptInfoResponse: 包含查询到的部门信息的响应对象。
//	error: 如果查询过程中出现错误，返回错误信息；否则返回nil。
func (s *SystemService) GetDeptInfo(ctx context.Context, req *systemService.DeptInfoRequest) (*systemService.DeptInfoResponse, error) {
	// 参数校验
	if req.Id <= 0 {
		return nil, status.Error(codes.InvalidArgument, "部门ID不能为空")
	}

	dept, err := s.dao.GetDepartmentById(ctx, req.Id)
	if err != nil {
		return nil, status.Error(codes.Internal, err.Error())
	}

	if dept == nil {
		return nil, status.Error(codes.NotFound, "部门不存在")
	}

	statusInt := int32(dept.Status)
	deptInfo := &systemService.DeptInfoResponse{
		Id:       &dept.Id,
		Name:     &dept.Name,
		ParentId: &dept.ParentId,
		SortNo:   &dept.SortNo,
		Status:   &statusInt,
		Remark:   &dept.Remark,
	}

	return deptInfo, nil
}
